﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="SendEmailExtended.cs" company="Open Source">
//   MIT License
// </copyright>
// <summary>
//   The Extended "send email" workflow type.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace Contour.Contrib.WorkflowTypes
{
    using System;
    using System.Collections.Generic;

    using System.Linq;
    using System.Net.Mail;
    using System.Text.RegularExpressions;
    using System.Web;
    using System.Web.Configuration;
    using System.Xml;
    using System.Xml.XPath;
    using System.Text;

    using umbraco;
    using umbraco.BusinessLogic;
    using umbraco.cms.businesslogic;
    using umbraco.cms.businesslogic.web;

    using Umbraco.Forms.Core;
    using Umbraco.Forms.Core.Enums;
    using Umbraco.Forms.Data.Storage;

    /// <summary>
    /// The send email extended.
    /// </summary>
    public class SendEmailExtended : WorkflowType
    {
        #region Constructors and Destructors

        /// <summary>
        /// Initializes a new instance of the <see cref="SendEmailExtended"/> class.
        /// </summary>
        public SendEmailExtended()
        {
            this.Id = new Guid("{0EF2E3DF-057B-4739-A781-C43E313B9E9C}");
            this.Name = "Send email, extended";
            this.Description =
                "Send the result of the form to 1 or multiple email address (separated with a semi-colon: ';'). For multi-lingual mails you can use the dash notation. For the Message field you can use multiple dictionary items (without spaces only) and newlines can be obtained using \n. To reference a field from your form use a lower-cased, spaceless version of your field name inside curly brackets like: {emailaddress}.";
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets or sets Email.
        /// </summary>
        [Umbraco.Forms.Core.Attributes.Setting("Email", description = "Enter the receiver email", control = "Umbraco.Forms.Core.FieldSetting.TextField")]
        public string Email { get; set; }

        /// <summary>
        /// Gets or sets From Email.
        /// </summary>
        [Umbraco.Forms.Core.Attributes.Setting("From Email", description = "Enter the from email (optional)", control = "Umbraco.Forms.Core.FieldSetting.TextField")]
        public string FromEmail { get; set; }

        /// <summary>
        /// Gets or sets From Name.
        /// </summary>
        [Umbraco.Forms.Core.Attributes.Setting("From Name", description = "Enter the from name (optional)", control = "Umbraco.Forms.Core.FieldSetting.TextField")]
        public string FromName { get; set; }

        /// <summary>
        /// Gets or sets ReplyTo.
        /// </summary>
        [Umbraco.Forms.Core.Attributes.Setting("ReplyTo", description = "Enter the reply-to email", control = "Umbraco.Forms.Core.FieldSetting.TextField")]
        public string ReplyTo { get; set; }

        /// <summary>
        /// Gets or sets Subject.
        /// </summary>
        [Umbraco.Forms.Core.Attributes.Setting("Subject", description = "Enter the subject", control = "Umbraco.Forms.Core.FieldSetting.TextField")]
        public string Subject { get; set; }

        /// <summary>
        /// Gets or sets Message.
        /// </summary>
        [Umbraco.Forms.Core.Attributes.Setting("Message", description = "Enter the intro message", control = "Umbraco.Forms.Core.FieldSetting.TextArea")]
        public string Message { get; set; }

        /// <summary>
        /// Gets or sets SendFields.
        /// </summary>
        [Umbraco.Forms.Core.Attributes.Setting("SendFields", description = "Send the content of the form fields in the email", control = "Umbraco.Forms.Core.FieldSetting.Checkbox")]
        public string SendFields { get; set; }

        /// <summary>
        /// Gets or sets Attachment.
        /// </summary>
        [Umbraco.Forms.Core.Attributes.Setting("Attachment", description = "Attach file uploads to email", control = "Umbraco.Forms.Core.FieldSetting.Checkbox")]
        public string Attachment { get; set; }

        #endregion

        #region Public Methods

        /// <summary>
        /// The execute.
        /// </summary>
        /// <param name="record">
        /// The record.
        /// </param>
        /// <param name="e">
        /// The Record event arguments.
        /// </param>
        /// <returns>
        /// The Workflow execution status
        /// </returns>
        public override WorkflowExecutionStatus Execute(Record record, RecordEventArgs e)
        {
            try
            {
                MailAddress FromMailAddress;
                if (string.IsNullOrEmpty(this.FromEmail))
                {
                    FromMailAddress = new MailAddress(UmbracoSettings.NotificationEmailSender);
                }
                else
                {
                    if (string.IsNullOrEmpty(this.FromName))
                    {
                        FromMailAddress = new MailAddress(this.FromEmail);
                    }
                    else
                    {
                        FromMailAddress = new MailAddress(this.FromEmail, this.FromName);
                    }
                }
                var mailMessage = new MailMessage
                    {
                        From = FromMailAddress,
                        Subject = this.Subject, 
                        ReplyTo = new MailAddress(this.ReplyTo), 
                        IsBodyHtml = true,
                        BodyEncoding = Encoding.UTF8
                    };

                if (this.Email.Contains(';'))
                {
                    foreach (string email in this.Email.Split(';'))
                    {
                        mailMessage.To.Add(email.Trim());
                    }
                }
                else
                {
                    mailMessage.To.Add(this.Email);
                }

                int lang = GetLanguageIdFromNodeId(record.UmbracoPageId);

                mailMessage.Body = "<p>" + ReplaceDictionaryItems(this.Message, lang).Replace(@"\n", "<br/>") + "</p>";

                if (this.SendFields == true.ToString())
                {
                    mailMessage.Body += this.CreateList(record, mailMessage, lang);
                }

                var smtpClient = new SmtpClient { EnableSsl = false };

                if (WebConfigurationManager.AppSettings.AllKeys.Contains("contourContribUseSsl") && WebConfigurationManager.AppSettings["contourContribUseSsl"].ToLower() == true.ToString().ToLower()) 
                {
                    smtpClient.EnableSsl = true;
                }

                smtpClient.Send(mailMessage);
            }
            catch (Exception ex)
            {
                Log.Add(LogTypes.Debug, -1, ex.Message);
            }

            return WorkflowExecutionStatus.Completed;
        }

        /// <summary>
        /// The validate settings.
        /// </summary>
        /// <returns>
        /// An exception list.
        /// </returns>
        public override List<Exception> ValidateSettings()
        {
            var exceptions = new List<Exception>();
            if (string.IsNullOrEmpty(this.ReplyTo))
            {
                exceptions.Add(new Exception("'Reply-to' setting not filled out'"));
            }

            if (string.IsNullOrEmpty(this.Email))
            {
                exceptions.Add(new Exception("'Email' setting not filled out'"));
            }

            if (string.IsNullOrEmpty(this.Message))
            {
                exceptions.Add(new Exception("'Message' setting not filled out'"));
            }

            return exceptions;
        }

        #endregion

        #region Methods

        /// <summary>
        /// The get dictionary item.
        /// </summary>
        /// <param name="item">
        /// The dictionary key
        /// </param>
        /// <param name="lang">
        /// The language
        /// </param>
        /// <returns>
        /// The returned dictionary item
        /// </returns>
        private static string GetDictionaryItem(string item, int lang)
        {
            // multi word dictionary items are enclosed in square brackets
            string translation = new Dictionary.DictionaryItem(item).Value(lang);

            // need to check for empty string to show keyname when item not translated
            if (string.IsNullOrEmpty(translation))
            {
                translation = item;
            }

            return translation;
        }

        /// <summary>
        /// The get language id from node id.
        /// </summary>
        /// <param name="nodeId">
        /// The node id.
        /// </param>
        /// <returns>
        /// The language id from node id.
        /// </returns>
        private static int GetLanguageIdFromNodeId(int nodeId)
        {
            Domain[] domains = library.GetCurrentDomains(nodeId);

            if (domains != null && domains.Length > 0)
            {
                return domains[0].Language.id;
            }

            return 0;
        }

        /// <summary>
        /// The replace dictionary items.
        /// </summary>
        /// <param name="content">
        /// The content.
        /// </param>
        /// <param name="lang">
        /// The language
        /// </param>
        /// <returns>
        /// The replace dictionary items
        /// </returns>
        private static string ReplaceDictionaryItems(string content, int lang)
        {
            const string regexPattern = @"(?<!\w)#(?<dic>\w+)";
            MatchCollection tagMatches = Regex.Matches(content, regexPattern);

            foreach (Match match in tagMatches)
            {
                string tagName = match.Groups["dic"].Value;
                string replacement = GetDictionaryItem(tagName, lang);
                content = content.Replace(match.Value, replacement);
            }

            return content;
        }

        /// <summary>
        /// The create list.
        /// </summary>
        /// <param name="record">
        /// The record.
        /// </param>
        /// <param name="mailMessage">
        /// The mail message.
        /// </param>
        /// <param name="lang">
        /// The language
        /// </param>
        /// <returns>
        /// An HTML list
        /// </returns>
        private string CreateList(Record record, MailMessage mailMessage, int lang)
        {
            var viewer = new RecordsViewer();
            XmlNode xml = viewer.GetSingleXmlRecord(record, new XmlDocument());
            XPathNavigator navigator = xml.CreateNavigator();
            XPathExpression selectExpression = navigator.Compile("//fields/child::*");
            selectExpression.AddSort("@pageindex", XmlSortOrder.Ascending, XmlCaseOrder.None, string.Empty, XmlDataType.Number);
            selectExpression.AddSort(
                "@fieldsetindex", XmlSortOrder.Ascending, XmlCaseOrder.None, string.Empty, XmlDataType.Number);
            selectExpression.AddSort("@sortorder", XmlSortOrder.Ascending, XmlCaseOrder.None, string.Empty, XmlDataType.Number);
            XPathNodeIterator nodeIterator = navigator.Select(selectExpression);
            string list = "<dl>";
            while (nodeIterator.MoveNext())
            {
                if (nodeIterator.Current == null)
                {
                    continue;
                }

                XPathNavigator node = nodeIterator.Current.SelectSingleNode(".//value");
                if (this.Attachment == true.ToString() && node != null &&
                    node.Value.Contains("/umbraco/plugins/umbracoContour/files/"))
                {
                    // add attachment
                    string filelocation = HttpContext.Current.Server.MapPath(node.Value);
                    mailMessage.Attachments.Add(new Attachment(filelocation));
                }
                else
                {
                    XPathNavigator captionNode = nodeIterator.Current.SelectSingleNode("caption");
                    if (captionNode != null)
                    {
                        string caption = captionNode.Value;

                        if (caption.StartsWith("#"))
                        {
                            caption = GetDictionaryItem(caption.TrimStart('#'), lang);
                        }

                        list += "<dt><strong>" + caption + ": </strong><dt><dd>";
                    }

                    XPathNodeIterator values = nodeIterator.Current.Select(".//value");

                    while (values.MoveNext())
                    {
                        if (values.Current != null)
                        {
                            list += values.Current.Value.Replace(Environment.NewLine, "<br />").Trim() + "<br />";
                        }
                    }

                    list += "</dd>";
                }
            }

            list += "</dl>";
            return list;
        }

        #endregion
    }
}